﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text.RegularExpressions;

namespace SemantEx.Tests
{
	[TestClass]
	public class BuilderTests
	{
		[TestMethod]
		public void Can_build_constants()
		{
			Assert.AreEqual(@"Hello\sWorld", new RegexBuilder()
				.Constant("Hello World")
				.Build());
		}

		[TestMethod]
		public void Can_build_number_range()
		{
			Assert.AreEqual("[0-9]", new RegexBuilder()
				.Range('0', '9')
				.Build());
		}

		[TestMethod]
		public void Can_build_an_arbitrary_collection()
		{
			Assert.AreEqual(@"[\-\s0-9_a-z]", new RegexBuilder()
				.BeginCollection('-', ' ').Range('0', '9').Constant('_').Range('a', 'z').EndCollection
				.Build());
			Assert.AreEqual(@"[Hello\sWorld]", new RegexBuilder()
				.BeginCollection().Constant("Hello World").EndCollection
				.Build());
		}

		[TestMethod]
		public void Can_build_repeating_number_range()
		{
			Assert.AreEqual("[0-9]{5}", new RegexBuilder()
				.Range('0', '9').Repeat(5)
				.Build());
		}

		//[TestMethod]
		//public void Can_build_empty_group()
		//{
		//    Assert.AreEqual("[0-9]{5}()", new RegexBuilder()
		//        .Range('0', '9').Repeat(5)
		//        .BeginGroup.EndGroup
		//        .Build());
		//}

		//[TestMethod]
		//public void Can_build_character_collection()
		//{
		//    Assert.AreEqual(@"[0-9]{5}([\-\s])", new RegexBuilder()
		//        .Range('0', '9').Repeat(5)
		//        .BeginGroup.Collection('-', ' ').EndGroup
		//        .Build());
		//}

		[TestMethod]
		public void Can_build_zip_code()
		{
			Assert.Fail("Not implemented yet.");
		}
	}

	public interface IRegexBuilder
	{
		IRegexBuilder Parent { get; set; }
		IEnumerable<IRegexBuilder> Children { get; set; }

		string Build();
	}

	public interface IRegexBuilder<TParent>
		where TParent : class, IRegexBuilder
	{
		TParent Parent { get; set; }
	}

	public interface IIncludeConstants<TParent>
		where TParent : IRegexBuilder
	{
		TParent Constant(params char[] chars);
		TParent Constant(string text);
	}

	public interface IIncludeRanges<TParent>
		where TParent : IRegexBuilder
	{
		TParent Range(char first, char last);
	}

	public interface IIncludeRepeat<TParent>
		where TParent : IRegexBuilder
	{
		TParent Repeat(int minRepeat, int maxRepeat);
		TParent Repeat(int maxRepeat);
		TParent MayHaveMany { get; }
		TParent MustHaveMany { get; }
		TParent MayHaveOne { get; }
	}

	public abstract class RegexBuilderBase : IRegexBuilder
	{
		#region Constructors

		public RegexBuilderBase(IRegexBuilder parent)
		{
			Parent = null;
			Children = Enumerable.Empty<IRegexBuilder>();
		}

		#endregion

		#region Properties

		public IRegexBuilder Parent { get; set; }

		public IEnumerable<IRegexBuilder> Children { get; set; }

		#endregion

		#region Methods

		public virtual string Build()
		{
			return Children.Aggregate(string.Empty, (full, single) => full += single.Build());
		}

		#endregion
	}

	public abstract class RegexBuilderBase<TParent> : RegexBuilderBase
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public RegexBuilderBase(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Properties

		public new TParent Parent
		{
			get
			{
				return base.Parent as TParent;
			}
			set
			{
				base.Parent = value;
			}
		}

		#endregion
	}

	public interface IRootBuilder : IRegexBuilder, IIncludeConstants<RegexBuilder>, IIncludeRanges<RegexBuilder>
	{
		Regex Finalize();
		CollectionBuilder<RegexBuilder> BeginCollection(params char[] chars);
		CollectionBuilder<RegexBuilder> BeginCollection(string text);
	}

	public class RegexBuilder : RegexBuilderBase, IRootBuilder
	{
		#region Constructors

		public RegexBuilder()
			: base(null)
		{
		}

		#endregion

		#region Methods

		public Regex Finalize()
		{
			return new Regex(Build());
		}

		public CollectionBuilder<RegexBuilder> BeginCollection(params char[] chars)
		{
			var collection = new CollectionBuilder<RegexBuilder>(this).Constant(chars);
			Children = Children.Concat(new[] { collection });
			return collection;
		}
		
		public CollectionBuilder<RegexBuilder> BeginCollection(string text)
		{
			return BeginCollection(text.ToArray());
		}

		public RegexBuilder Range(char first, char last)
		{
			return BeginCollection().Range(first, last).EndCollection;
		}

		public RegexBuilder Constant(params char[] chars)
		{
			Children = Children.Concat(chars.Select(x => new ConstantBuilder(this, x)));
			return this;
		}

		public RegexBuilder Constant(string text)
		{
			return Constant(text.ToArray());
		}

		#endregion
	}

	public class MayHaveOneBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public MayHaveOneBuilder(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Methods

		public override string Build()
		{
			return base.Build() + "?";
		}

		#endregion
	}

	public class MayHaveManyBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public MayHaveManyBuilder(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Methods

		public override string Build()
		{
			return base.Build() + "*";
		}

		#endregion
	}

	public class MustHaveManyBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public MustHaveManyBuilder(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Methods

		public override string Build()
		{
			return base.Build() + "+";
		}

		#endregion
	}

	public class RepeatBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public RepeatBuilder(TParent parent, int? minRepeat, int maxRepeat)
			: base(parent)
		{
			MinRepeat = minRepeat;
			MaxRepeat = maxRepeat;
		}

		public RepeatBuilder(TParent parent, int maxRepeat)
			: this(parent, null, maxRepeat)
		{
		}

		#endregion

		#region Properties

		public int? MinRepeat { get; set; }

		public int MaxRepeat { get; set; }

		#endregion

		#region Methods

		public override string Build()
		{
			if (MinRepeat.HasValue)
			{
				return base.Build() + string.Format("{{{0},{1}}}", MinRepeat.Value, MaxRepeat);
			}
			return base.Build() + string.Format("{{{0}}}", MaxRepeat);
		}

		#endregion
	}

	public class RepetitionBuilder<TParent> : RegexBuilderBase<TParent>, TParent, IIncludeRepeat<TParent>, IRootBuilder
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		public RepetitionBuilder(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Properties

		public TParent MayHaveOne
		{
			get
			{
				var repeat = new MayHaveOneBuilder<TParent>(Parent);
				repeat.Children = repeat.Children.Concat(Children);
				return repeat.Parent;
			}
		}

		public TParent MayHaveMany
		{
			get
			{
				var repeat = new MayHaveManyBuilder<TParent>(Parent);
				repeat.Children = repeat.Children.Concat(Children);
				return repeat.Parent;
			}
		}

		public TParent MustHaveMany
		{
			get
			{
				var repeat = new MustHaveManyBuilder<TParent>(Parent);
				repeat.Children = repeat.Children.Concat(Children);
				return repeat.Parent;
			}
		}

		#endregion

		#region Methods

		public TParent Repeat(int minRepeat, int maxRepeat)
		{
			var repeat = new RepeatBuilder<TParent>(Parent, minRepeat, maxRepeat);
			repeat.Children = repeat.Children.Concat(Children);
			return repeat.Parent;
		}

		public TParent Repeat(int maxRepeat)
		{
			var repeat = new RepeatBuilder<TParent>(Parent, maxRepeat);
			repeat.Children = repeat.Children.Concat(Children);
			return repeat.Parent;
		}

		public override string Build()
		{
			if (Children.Count() != 1)
			{
				throw new NotSupportedException("Must have 1 child.");
			}
			return base.Build();
		}

		#endregion
	}

	public class ConstantBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		internal ConstantBuilder(TParent parent, char ch)
			: base(parent)
		{
			Character = Process(ch);
		}

		#endregion

		#region Properties

		public string Character { get; set; }

		#endregion

		#region Methods

		public override string Build()
		{
			return string.Format("{0}{1}", base.Build(), Character);
		}

		private string Process(char ch)
		{
			switch (ch)
			{
				case ' ':
					return @"\s";
				case '-':
					return @"\-";
				default:
					return ch.ToString();
			}
		}

		#endregion
	}

	public class CollectionBuilder<TParent> : RegexBuilderBase<TParent>, IIncludeConstants<CollectionBuilder<TParent>>, IIncludeRanges<CollectionBuilder<TParent>>, IIncludeRepeat<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		internal CollectionBuilder(TParent parent)
			: base(parent)
		{
		}

		#endregion

		#region Properties

		public TParent EndCollection
		{
			get
			{
				return Parent;
			}
		}

		public TParent MayHaveOne
		{
			get
			{
				Children = Children.Concat(new[] { new MayHaveOneBuilder<CollectionBuilder<TParent>>(this) });
				return Parent;
			}
		}

		public TParent MayHaveMany
		{
			get
			{
				Children = Children.Concat(new[] { new MayHaveManyBuilder<CollectionBuilder<TParent>>(this) });
				return Parent;
			}
		}

		public TParent MustHaveMany
		{
			get
			{
				Children = Children.Concat(new[] { new MustHaveManyBuilder<CollectionBuilder<TParent>>(this) });
				return Parent;
			}
		}

		#endregion

		#region Methods

		public TParent Repeat(int minRepeat, int maxRepeat)
		{
			Children = Children.Concat(new[] { new RepeatBuilder<CollectionBuilder<TParent>>(this, minRepeat, maxRepeat) });
			return Parent;
		}

		public TParent Repeat(int maxRepeat)
		{
			Children = Children.Concat(new[] { new RepeatBuilder<CollectionBuilder<TParent>>(this, maxRepeat) });
			return Parent;
		}

		public CollectionBuilder<TParent> Range(char first, char last)
		{
			var range = new RangeBuilder(this, first, last);
			Children = Children.Concat(new[] { range });
			return this;
		}

		public CollectionBuilder<TParent> Constant(params char[] chars)
		{
			Children = Children.Concat(chars.Select(x => new ConstantBuilder<CollectionBuilder<TParent>>(this, x)));
			return this;
		}
		
		public CollectionBuilder<TParent> Constant(string text)
		{
			return Constant(text.ToArray());
		}

		public override string Build()
		{
			return string.Format("[{0}]", base.Build());
		}

		#endregion
	}

	public class RangeBuilder<TParent> : RegexBuilderBase<TParent>
		where TParent : class, IRegexBuilder
	{
		#region Constructors

		internal RangeBuilder(TParent parent, char first, char last)
			: base(parent)
		{
			First = first;
			Last = last;
		}

		#endregion

		#region Properties

		public char First { get; set; }

		public char Last { get; set; }

		#endregion

		#region Methods

		public override string Build()
		{
			return string.Format("{0}-{1}", First, Last);
		}

		#endregion
	}

	//public class RepeatBuilder
	//{
	//    internal RepeatBuilder(string regex, int count)
	//    {
	//        Regex = regex;
	//        Count = count;
	//    }

	//    private string Regex { get; set; }

	//    public int Count { get; set; }

	//    public GroupBuilder BeginGroup
	//    {
	//        get
	//        {
	//            return new GroupBuilder(Build());
	//        }
	//    }

	//    public string Build()
	//    {
	//        return string.Format("{0}{{{1}}}", Regex, Count);
	//    }
	//}

	//public class GroupBuilder
	//{
	//    internal GroupBuilder(string regex)
	//    {
	//        Regex = regex;
	//    }

	//    private string Regex { get; set; }

	//    public GroupBuilder EndGroup
	//    {
	//        get
	//        {
	//            return new GroupBuilder(string.Format("{0}()", Build()));
	//        }
	//    }

	//    public CollectionBuilder Collection(params char[] parms)
	//    {
	//        return new CollectionBuilder(Build(), parms);
	//    }

	//    public string Build()
	//    {
	//        return Regex;
	//    }
	//}
}